package cn.janescott.inner.space.webapp.config;

import cn.janescott.inner.space.shiro.filter.CustomFormAuthenticationFilter;
import cn.janescott.inner.space.shiro.realm.DatabaseShiroRealm;
import cn.janescott.inner.space.webapp.adapter.DatabaseRealmHandler;
import cn.janescott.inner.space.webapp.adapter.MyPasswordAdapter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.config.ShiroBeanConfiguration;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroWebConfiguration;
import org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.jasypt.util.password.BasicPasswordEncryptor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.List;

/**
 * @author scott
 * @date 2018/7/26
 * @see ShiroBeanConfiguration#eventBus() 提供一个默认的({@link org.apache.shiro.event.EventBus})
 * @see ShiroWebFilterConfiguration#shiroFilterFactoryBean() 提供默认的ShiroFilterFactoryBean
 * @see ShiroWebConfiguration 提供web环境下的以下功能
 * 认证 && 授权 (必须手动添加 {@link org.apache.shiro.realm.Realm})
 * 缓存(如果需要，需手动添加 {@link org.apache.shiro.cache.CacheManager})
 * session(默认提供{@link org.apache.shiro.web.session.mgt.ServletContainerSessionManager}, {@link org.apache.shiro.session.mgt.SimpleSessionFactory})
 * rememberMe(默认提供{@link org.apache.shiro.web.mgt.CookieRememberMeManager}，可以手动实现{@link org.apache.shiro.mgt.RememberMeManager})
 */
@Configuration
public class ShiroSecurityConfiguration {

    @Bean
    public SecurityManager securityManager(List<Realm> realms) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealms(realms);
        securityManager.setSessionManager(new ServletContainerSessionManager());
        return securityManager;
    }

    /**
     * 数据库处理器
     * @param handler 处理器
     */
    @Bean
    public DatabaseShiroRealm dbRealm(DatabaseRealmHandler handler) {
        return new DatabaseShiroRealm().setRealmHandler(handler).setPasswordMatcher(passwordHandler());
    }

    /**
     * 加密 && 校验
     */
//    @Bean
//    public StrongPasswordEncryptor encryptor() {
//        return new StrongPasswordEncryptor();
//    }
    @Bean
    public BasicPasswordEncryptor encryptor() {
        return new BasicPasswordEncryptor();
    }

    /**
     * 密码校验器
     */
    @Bean
    public MyPasswordAdapter passwordHandler() {
        return new MyPasswordAdapter().setEncryptor(encryptor());
    }

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------- web 项目需要添加 相关filter -------------------------------------------------
//---------------------参考ShiroWebFilterConfiguration---------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

    @Bean("authc")
    public FormAuthenticationFilter formAuthenticationFilter() {
        return new CustomFormAuthenticationFilter("/login");
    }

    /**
     * anon（匿名）  org.apache.shiro.web.filter.authc.AnonymousFilter
     * authc（身份验证）       org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     * authcBasic（http基本验证）    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
     * logout（退出）        org.apache.shiro.web.filter.authc.LogoutFilter
     * noSessionCreation（不创建session） org.apache.shiro.web.filter.session.NoSessionCreationFilter
     * perms(许可验证)  org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
     * port（端口验证）   org.apache.shiro.web.filter.authz.PortFilter
     * rest  (rest方面)  org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
     * roles（权限验证）  org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
     * ssl （ssl方面）   org.apache.shiro.web.filter.authz.SslFilter
     * member （用户方面）  org.apache.shiro.web.filter.authc.UserFilter
     * user  表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        //<!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        //anon 可以理解为不拦截
        chainDefinition.addPathDefinition("/resources/**", "anon");
        chainDefinition.addPathDefinition("/demo/**", "anon");
        chainDefinition.addPathDefinition("/actuator/**", "anon");
        //logout 登出
        chainDefinition.addPathDefinition("/logout", "logout");
        //身份验证
        chainDefinition.addPathDefinition("/login", "authc");
        //权限
        chainDefinition.addPathDefinition("/**", "user");
        return chainDefinition;
    }

    /**
     * shiro自带的过滤器枚举
     * {@link org.apache.shiro.web.filter.mgt.DefaultFilter}
     *
     * @see org.springframework.beans.factory.FactoryBean 实现了此接口，getObject方法返回Filter实例
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager, ShiroFilterChainDefinition chainDefinition) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.getFilters().put("authc", formAuthenticationFilter());

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的连接
        shiroFilterFactoryBean.setSuccessUrl("/home");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(chainDefinition.getFilterChainMap());
        return shiroFilterFactoryBean;
    }

    /**
     * 为了解决 UnavailableSecurityManagerException
     * {@literal https://www.cnblogs.com/ginponson/p/6217057.html}
     */
    @Bean
    @SuppressWarnings("unchecked")
    public FilterRegistrationBean delegatingFilterProxy() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

}
